package org.dromara.demo.controller;

import org.dromara.common.redis.utils.RedisUtils;

import java.time.Duration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author 冉凯凯
 */
public class LogicalExpirationCacheDemo {

    // 线程池用于异步刷新缓存
    private static ExecutorService executor = Executors.newFixedThreadPool(5);

    // 分布式锁Key前缀
    private static final String LOCK_PREFIX = "lock:";


    private static final String DATA = "data";


    // 逻辑过期时间字段名
    private static final String LOGIC_EXPIRE_FIELD = "logicExpireTime";

    // 默认缓存物理过期时间（兜底）// 1小时
    private static final int DEFAULT_PHYSICAL_TTL = 3600;

    //获取数据


    public static void main(String[] args) throws InterruptedException {
        // 测试逻辑
        String key = "product:1001";

        // 第一次查询：触发缓存重建
        System.out.println("第一次查询: " + getData(key));

        // 模拟缓存逻辑过期
        long lockTime= System.currentTimeMillis() - 1000;

        RedisUtils.setCacheMapValue(key, LOGIC_EXPIRE_FIELD, Duration.ofMinutes(lockTime));
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 查询结果: " + getData(key));
            }).start();
        }
        // 等待异步刷新完成
        Thread.sleep(2000);
        System.out.println("最终缓存内容: " + RedisUtils.getCacheObject(key));
        executor.shutdown();
    }

    /**
     * 获取缓存数据（核心逻辑）
     */
    public static String getData(String key) {
        // 1. 从缓存获取数据
        String cacheData = RedisUtils.getCacheMapValue(key, DATA);

        String logicExpireStr = RedisUtils.getCacheMapValue(key, LOGIC_EXPIRE_FIELD);

        // 2. 缓存不存在则初始化
        if (cacheData == null) {
            return rebuildCache(key);
        }

        // 3. 检查逻辑过期时间
        long logicExpireTime = Long.parseLong(logicExpireStr);
        if (logicExpireTime <= System.currentTimeMillis()) {
            // 4. 异步刷新缓存
            executor.execute(() -> {
                // 获取分布式锁（防并发重建）
                String lockKey = LOCK_PREFIX + key;
                if (RedisUtils.setObjectIfAbsent(lockKey, "", Duration.ofMillis(100000L))) {
                    try {
                        System.out.println("---> 开始异步刷新缓存: " + key);
                        rebuildCache(key);
                    } finally {
                        // 释放锁
                        RedisUtils.deleteObject(lockKey);
                    }
                }
            });
        }

        return cacheData;
    }
    /**
     * 重建缓存（模拟数据库查询）
     */
    private static String rebuildCache(String key) {
        try {
            // 模拟数据库查询耗时
            Thread.sleep(1000);

            // 生成新数据（这里模拟实际业务数据）
            String newData = "最新数据@" + System.currentTimeMillis();

            // 设置逻辑过期时间（30分钟后过期）
            long newLogicExpire = System.currentTimeMillis() + 30 * 60 * 1000;

            // 更新缓存（设置物理TTL兜底）
            RedisUtils.setCacheMapValue(key, DATA, newData);
            RedisUtils.setCacheMapValue(key, LOGIC_EXPIRE_FIELD, newLogicExpire);
            RedisUtils.expire(key ,DEFAULT_PHYSICAL_TTL);

            return newData;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    // 业务数据变更时，直接更新缓存并重置逻辑过期时间，或标记为过期（logicExpireTime = 当前时间），强制下次查询触发重建。
}
